package com.cityu.bac.rfidDriver;


import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.util.Timer;
import java.util.TimerTask;

import android.util.Log;

import com.cityu.bac.rfidDriver.RfidDriver.DeviceType;


class CommandProcess {
	private static final String TAG = "CommandProcess";
	
	protected SerialPort serialPort;
    protected PacketProcess pktProcess;
 	protected DeviceType deviceType = DeviceType.UsbReader;

	
//	public CommandProcess(String devicePath,int baudrate) throws Exception{
//		serialPort = new SerialPort(new File(devicePath),baudrate);
//		pktProcess = new PacketProcess(this,serialPort);
//		
//	
    
    protected Timer timer = new Timer();
    protected ReadBarcodeTask readBarcodeTask = null;
    
    protected enum Function{
        RFID,
        Barcode,
    };
    Function curFunction = Function.RFID;
    
    SerialPort getSerialPort() {
        return serialPort;
    }

    void setSerialPort(SerialPort serialPort) {
        this.serialPort = serialPort;
    }

    PacketProcess getPktProcess() {
        return pktProcess;
    }

    void setPktProcess(PacketProcess pktProcess) {
        this.pktProcess = pktProcess;
    }
	
	protected byte[] sendCommand(byte[] data,long timeout) throws Exception{
	    return sendCommand(data);
	}
	
	protected byte[] sendCommand(byte[] data) throws Exception{
	    byte[] resp = null;
	    pktProcess.sendCommand(data);
	    resp = pktProcess.getResponse(data[2]);
	    return resp;
	}
	
	/**********************************************************************
     * 
     * Check Reading
     * 
     **********************************************************************/
	public  boolean isReading()
    {
        return pktProcess.isReading();
    }
	
	/**********************************************************************
     * 
     * Start Reading
	 * @throws Exception 
     * 
     **********************************************************************/
    public void startRead() throws Exception
    {
        Log.d(TAG,"Entry to startRead...");
        
        if(deviceType == DeviceType.MiniReader){
        	pressButton();
        	pktProcess.setReading(true);
        	return;
        }

        
        if (pktProcess.isReading())
        {
        	Log.d(TAG,"Already reading from RFID reader, ignoring read request...");
            return;
        }

        byte[] resp = sendCommand(Command.cmdStart());
        Log.d(TAG,"resp: " + Utils.ByteToHex(resp));
        if (!Command.respokStart(resp))
        {
//             throw new Exception("Unable to startRead...");
        }
        
        pktProcess.setReading(true);
    }
    
    /**********************************************************************
     * 
     * Stop Reading
     * @throws Exception 
     * 
     **********************************************************************/
    public void stopRead() throws Exception
    {

            Log.d(TAG,"Entry to stopRead...");
            
            if(deviceType == DeviceType.MiniReader){
            	releaseButton();
            	pktProcess.setReading(false);
            	return;
            }


            pktProcess.setReading(false);

            byte[] resp = sendCommand(Command.cmdStop());
            Log.d(TAG,"resp: " + Utils.ByteToHex(resp));
            if (!Command.respokStop(resp))
            {
                throw new Exception("Unable to stop read...");
            }
            

            Log.d(TAG,"Exit Stop");
    }
    
    /**********************************************************************
     * 
     * Restart RFID
     * @throws Exception 
     * 
     **********************************************************************/
    public void restart() throws Exception{
    	Log.d(TAG,"Restart RFID");
    	
    	//stop
    	pktProcess.setReading(false);
    	byte[] resp = sendCommand(Command.cmdStop());
        Log.d(TAG,"resp: " + Utils.ByteToHex(resp));
        if (!Command.respokStop(resp))
        {
            throw new Exception("Unable to stop read...");
        }
        
        resp = sendCommand(Command.cmdStart());
        Log.d(TAG,"resp: " + Utils.ByteToHex(resp));
        if (!Command.respokStart(resp))
        {
             throw new Exception("Unable to startRead...");
        }
        
        pktProcess.setReading(true);
        //start
        
    }
    
    /**********************************************************************
     * 
     * Read Target Memory
     * @throws Exception 
     * 
     **********************************************************************/
    public byte[] ReadTargetMemory(byte[] accessPasswd, byte[] uii, byte targetMem,short startAdr,short length) throws Exception
    {
        Log.d(TAG,"Entry to Read Target Memory...");
        ByteArrayOutputStream cmdbos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(cmdbos);
        dos.write(accessPasswd,0,accessPasswd.length);
        dos.writeShort(uii.length);
        dos.write(uii, 0, uii.length);
        dos.writeByte(targetMem);
        dos.writeShort(startAdr);
        dos.writeShort(length);
        dos.flush();
        
        byte cmd[] = Command.buildCommand(Command.typeCommand, (byte) 0x39, cmdbos.toByteArray());
        Log.d(TAG,"write tag cmd: " + Utils.ByteToHex(cmd));
        byte[] resp = sendCommand(cmd,1000);
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        
        if(resp[2] == (byte)0xFF){
            return null;
        }else{
            byte[] data = new byte[resp.length-6];
            System.arraycopy(resp, 5, data, 0, data.length);
            return data;
        }        
    }
    
    /**********************************************************************
     * 
     * Write Target Memory
     * @throws Exception 
     * 
     **********************************************************************/
    public byte[] WriteTargetMemory(byte[] accessPasswd, byte[] uii, byte targetMem,short startAdr,short length,byte[] data) throws Exception
    {
        Log.d(TAG,"Entry to Write Target Memory...");
        ByteArrayOutputStream cmdbos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(cmdbos);
        dos.write(accessPasswd,0,accessPasswd.length);
        dos.writeShort(uii.length);
        dos.write(uii, 0, uii.length);
        dos.writeByte(targetMem);
        dos.writeShort(startAdr);
        dos.writeShort(length);
        dos.write(data,0,data.length);
        dos.flush();
        
        byte cmd[] = Command.buildCommand(Command.typeCommand, (byte) 0x49, cmdbos.toByteArray());
        Log.d(TAG,"write tag cmd: " + Utils.ByteToHex(cmd));
        byte[] resp = sendCommand(cmd,1000);
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        
        return resp;      
    }
    
    /**********************************************************************
     * 
     * Lock Tag
     * @throws Exception 
     * 
     **********************************************************************/
    public byte[] lockTag(byte[] accessPasswd, byte[] uii, byte[] lockData) throws Exception
    {
        Log.d(TAG,"Entry to Lock Tag...");
        ByteArrayOutputStream cmdbos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(cmdbos);
        dos.write(accessPasswd,0,accessPasswd.length);
        dos.writeShort(uii.length);
        dos.write(uii, 0, uii.length);
        dos.write(lockData,0,lockData.length);
        dos.flush();
        
        byte cmd[] = Command.buildCommand(Command.typeCommand, (byte) 0x82, cmdbos.toByteArray());
        Log.d(TAG,"write tag cmd: " + Utils.ByteToHex(cmd));
        byte[] resp = sendCommand(cmd,1000);
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        
        return resp;      
    }
    
    /**********************************************************************
     * 
     * Write Tag
     * @throws Exception 
     * 
     **********************************************************************/
    public byte[] writeTag(byte[] oldPC_EPC, byte[] newPC_EPC, byte[] accessPasswd) throws Exception
    {
        Log.d(TAG,"Entry to writeTag...");
        byte[] cmd = Command.cmdWrite(oldPC_EPC, newPC_EPC, accessPasswd);
        Log.d(TAG,"write tag cmd: " + Utils.ByteToHex(cmd));
        byte[] resp = sendCommand(cmd);
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        
        return resp;
    }

//    /**********************************************************************
//     * 
//     * Lock Tag
//     * 
//     **********************************************************************/
//    public void lockTag(byte[] PC_EPC, byte[] accessPasswd)
//    {
//        Log.d(TAG,"Entry to lock tag...");
//
//        return;
//    }

    /**********************************************************************
     * 
     * Kill Tag
     * @throws Exception 
     * 
     **********************************************************************/
    public void killTag(byte[] PC_EPC, byte[] killPasswd) throws Exception
    {
        Log.d(TAG,"Entry to killTag...");

        byte[] resp = sendCommand(Command.cmdKill(PC_EPC, killPasswd));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokKill(resp))
        {
            throw new Exception("Unable to kill tag...");
        } 
    }

    /**********************************************************************
     * 
     * Set Reader Power
     * @throws Exception 
     * 
     **********************************************************************/
    public void setPower(short power) throws Exception
    {
        Log.d(TAG,"Entry to setPower...");
        byte[] cmd = Command.cmdSetReaderPower(power);
        Log.d(TAG,"setPower cmd: " + Utils.ByteToHex(cmd));
        byte[] resp = sendCommand(cmd);
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetReaderPower(resp))
        {
            throw new Exception("Unable to set reader power...");
        }

    }

    /**********************************************************************
     * 
     * Get Reader Power
     * 
     **********************************************************************/
    public short getPower()
    {
        Log.d(TAG,"Entry to GetPower...");

        short power = 100;

        return power;
    }

    /**********************************************************************
     * 
     * Set Region
     * @throws Exception 
     * 
     **********************************************************************/
    public void setRegion(byte region) throws Exception
    {
        Log.d(TAG,"Entry to SetRegion...");

        byte[] resp = sendCommand(Command.cmdSetRegion(region));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetRegion(resp))
        {
            throw new Exception("Unable to set region...");
        }    
    }

    /**********************************************************************
     * 
     * Get Region
     * @throws Exception 
     * 
     **********************************************************************/
    public byte getRegion() throws Exception
    {
        Log.d(TAG,"Entry to getRegion...");

        byte[] resp = sendCommand(Command.cmdGetRegion());
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        byte region = Command.parseRegion(resp);
        if (region == 0)
        {
            Log.e(TAG,"Unable to get region properly");
        }
        return region;
    }

    /**********************************************************************
     * 
     * Get Channel
     * @throws Exception 
     * 
     **********************************************************************/
    public byte getChannel() throws Exception
    {
        Log.d(TAG,"Entry to getChannel...");

        byte[] resp = sendCommand(Command.cmdGetChannel());
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        byte cn = Command.parseChannel(resp);
        if (cn == 0)
        {
            Log.e(TAG,"Unable to get channel properly");
        }
        return cn;
    }

    /**********************************************************************
     * 
     * Set Channel
     * @throws Exception 
     * 
     **********************************************************************/
    public void setChannel(byte cn, byte cno) throws Exception
    {
        Log.d(TAG,"Entry to setChannel...");

        byte[] resp = sendCommand(Command.cmdSetChannel(cn, cno));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetChannel(resp))
        {
            throw new Exception("Unable to setChannel...");
        }
    }
    
    /**********************************************************************
     * 
     * Set Qry parm
     * @throws Exception 
     * 
     **********************************************************************/
    public void setQryParm(int qry) throws Exception //it's not stable
    {
        Log.d(TAG,"Entry to setQryParm...");
        if(true)
        	return;

        byte[] resp = sendCommand(Command.cmdSetQryParm(qry));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetQryParm(resp))
        {
            throw new Exception("Unable to setQryParm...");
        }
    }

    /**********************************************************************
     * 
     * Private Methods for Initial Configuration
     * 
     **********************************************************************/

    /**********************************************************************
     * 
     * Set PA Output Power
     * @throws Exception 
     * 
     **********************************************************************/
    @SuppressWarnings("unused")
    private void setPAPower(int power) throws Exception
    {
        Log.d(TAG,"Entry to setPAPower...");

        byte[] resp = sendCommand(Command.cmdSetPAPower((short)power));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetPAPower(resp))
        {
            throw new Exception("Unable to set PA power...");
        }
    }

    /**********************************************************************
     * 
     * Set PA Offset
     * @throws Exception 
     * 
     **********************************************************************/
    @SuppressWarnings("unused")
    private void setPAOffset(int offset) throws Exception
    {
        Log.d(TAG,"Entry to setPAOffset...");

        byte[] resp = sendCommand(Command.cmdSetPAOffset((short)offset));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetPAOffset(resp))
        {
            throw new Exception("Unable to set PA offset...");
        }
    }     

    /**********************************************************************
     * 
     * Set Power Class
     * @throws Exception 
     * 
     **********************************************************************/
    @SuppressWarnings("unused")
    private void setPowerClass(int powerClass) throws Exception
    {
        Log.d(TAG,"Entry to setPowerClass...");

        byte[] resp = sendCommand(Command.cmdSetPowerClass(powerClass));
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSetPowerClass(resp))
        {
            throw new Exception("Unable to set power class...");
        }
    }

    /**********************************************************************
     * 
     * Extended Methods
     * 
     * These methods are not part of PR9000.
     * 
     * They are extended by Sengital.
     * 
     **********************************************************************/
    /**********************************************************************
     * 
     * Switch Device to RFID
     * @throws Exception 
     * 
     **********************************************************************/
    public void switchToRFID() throws Exception
    {
        Log.d(TAG,"Entry to Switch Device to RFID...");
        curFunction = Function.RFID;
        byte[] resp = sendCommand(Command.cmdSwitchToRFID());
        if(resp == null){
            throw new Exception("Unable to switch to RFID Reader...");
        }
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSwitchDevice(resp))
        {
            throw new Exception("Unable to switch to RFID Reader...");
        }
    }
    
    /**********************************************************************
     * 
     * Switch Device to Barcodes
     * @throws Exception 
     * 
     **********************************************************************/
    public void switchToBarcode() throws Exception
    {
        Log.d(TAG,"Entry to Switch Device to Barcodes...");
        curFunction = Function.Barcode;
        byte[] resp = sendCommand(Command.cmdSwitchToBarcode());
        if(resp == null){
            return;
        }
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if (!Command.respokSwitchDevice(resp))
        {
            throw new Exception("Unable to switch to Barcode Reader...");
        }
    }

//    /**********************************************************************
//     * 
//     * Start Read Barcode
//     * 
//     **********************************************************************/
//    public void startReadBarcode()
//    {
//
//            if (pktProcess.isReading())
//            {
//                Log.d(TAG,"Already reading from RFID reader, ignoring read request...");
//                return;
//            }
//            
//            pktProcess.setReading(true);
//        
//    }
//
//    /**********************************************************************
//     * 
//     * Stop Read Barcode
//     * 
//     **********************************************************************/
//    public void stopReadBarcode()
//    {
//
//            if (!pktProcess.isReading())
//            {
//                Log.d(TAG,"Reader is not reading currently, igorning stopRead request");
//                return;
//            }
//
//            pktProcess.setReading(false);
//    }


    /**********************************************************************
     * 
     * Close the command processor and the serial port
     * 
     **********************************************************************/
    protected void close(){
        if(serialPort != null)
            serialPort.close();
        if(pktProcess != null)
            pktProcess.stopReadBytes(true);
        
        serialPort = null;
        pktProcess = null;
    }
    
    /**
     * Simulate to press the button
     * @throws Exception
     */
    public void pressButton() throws Exception
    {
        byte success[] = {(byte)0xbb, 0x01, 0x13, 0x00, 0x01, 0x01, 0x7e};
        Log.d(TAG,"Entry to Click Button ...");
        byte[] resp = sendCommand(Command.cmdClickButton());
        if(resp == null){
            return;
        }
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if(!Utils.equalbyteArr(success, resp)){
            throw new Exception("click button error!");
        }
        
        if(curFunction == Function.Barcode){
            readBarcodeTask = new ReadBarcodeTask();
            timer.schedule(readBarcodeTask, 3600, 3600);
        }
        Thread.sleep(50);
    }
    
    /**
     * Simulate to release the button
     * @throws Exception
     */
    public void releaseButton() throws Exception
    {
//        Log.d(TAG,"Entry to Release Button ...");
//        
//        if(curFunction == Function.Barcode || readBarcodeTask != null){
//            readBarcodeTask.cancel();
//        }
//        
//        byte[] resp = sendCommand(Command.cmdReleaseButton(),300);
//        if(resp == null){
//            return;
//        }
//        String hex = Utils.ByteToHex(resp);
//        Log.d(TAG,"resp: " + hex);
//        
//        
//        Thread.sleep(50);
        Log.d(TAG,"Entry to Release Button ...");
        byte success[] = {(byte)0xbb, 0x01, 0x13, 0x00, 0x01, 0x01, 0x7e};
        
        if(curFunction == Function.Barcode || readBarcodeTask != null){
            readBarcodeTask.cancel();
        }
        
        byte[] resp = sendCommand(Command.cmdReleaseButton());
        if(resp == null){
            return;
        }
        String hex = Utils.ByteToHex(resp);
        Log.d(TAG,"resp: " + hex);
        if(!Utils.equalbyteArr(success, resp)){
            throw new Exception("Release button error!");
        }
        
        Thread.sleep(50);
    }
    
    class ReadBarcodeTask extends TimerTask{

        @Override
        public void run() {
            try {
                sendCommand(Command.cmdReleaseButton());
                Thread.sleep(50);
                sendCommand(Command.cmdClickButton());
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        
    }
    
    
}
